package com.kj.report.utils;

import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis缓存
 *
 * 采用Jedis或Jedis Sentinel
 *
 * @author ZJW
 *
 */
@Component
public class RedisUtils {

	public final static String CAHCENAME = "cache";// 缓存名
	public final static int CAHCETIME = 60;// 默认缓存时间

	@Autowired
	private RedisTemplate<String, String> redisTemplate;

	private final static Gson gson = new Gson();

	/** 不设置过期时长 */
	public final static long NOT_EXPIRE = -1;

	public <T> boolean putCache(String key, T obj) {
		final byte[] bkey = key.getBytes();
		final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.setNX(bkey, bvalue);
			}
		});
		return result;
	}

	public void set(String key, Object value, long expire) {
		redisTemplate.opsForValue().set(key, toJson(value));
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.DAYS);
		}
	}
	
	public void setString(String key, String value, long expire) {
		redisTemplate.opsForValue().set(key, value);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.SECONDS);
		}

	}


	public <T> T get(String key, Class<T> clazz, long expire) {
		String value = redisTemplate.opsForValue().get(key);
		if (expire != NOT_EXPIRE) {
			redisTemplate.expire(key, expire, TimeUnit.DAYS);
		}
		return value == null ? null : fromJson(value, clazz);
	}
	
	public <T> T get(String key, Class<T> clazz) {
		String value = redisTemplate.opsForValue().get(key);
		return value == null ? null : fromJson(value, clazz);
	}
	
	public String get(String key) {
		String value = redisTemplate.opsForValue().get(key);
		return value == null ? null : value;
	}


	public <T> void putCacheWithExpireTime(String key, T obj,
			final long expireTime) {
		final byte[] bkey = key.getBytes();
		final byte[] bvalue = ProtoStuffSerializerUtil.serialize(obj);
		redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				connection.setEx(bkey, expireTime, bvalue);
				return true;
			}
		});
	}

	public <T> boolean putListCache(String key, List<T> objList) {
		deleteCache(key);
		final byte[] bkey = key.getBytes();
		final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);

		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.setNX(bkey, bvalue);
			}
		});
		return result;
	}

	public <T> boolean putListCacheWithExpireTime(String key, List<T> objList,
			final long expireTime) {
		final byte[] bkey = key.getBytes();
		final byte[] bvalue = ProtoStuffSerializerUtil.serializeList(objList);
		boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection connection)
					throws DataAccessException {
				connection.setEx(bkey, expireTime, bvalue);
				return true;
			}
		});
		return result;
	}

	public <T> T getCache(final String key, Class<T> targetClass) {
		byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
			@Override
			public byte[] doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.get(key.getBytes());
			}
		});
		if (result == null) {
			return null;
		}
		return ProtoStuffSerializerUtil.deserialize(result, targetClass);
	}

	public <T> List<T> getListCache(final String key, Class<T> targetClass) {
		byte[] result = redisTemplate.execute(new RedisCallback<byte[]>() {
			@Override
			public byte[] doInRedis(RedisConnection connection)
					throws DataAccessException {
				return connection.get(key.getBytes());
			}
		});
		if (result == null) {
			return null;
		}
		return ProtoStuffSerializerUtil.deserializeList(result, targetClass);
	}

	public boolean putMap(String key, Map<String, Object> map) {
		try {
			redisTemplate.opsForHash().putAll(key, map);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * set方式存储
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean sAdd(String key, String value) {
		try {
			redisTemplate.opsForSet().add(key, value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}
	
	
	/**
	 * set获取
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Set<String> sGet(String key, String value) {
			
	return redisTemplate.opsForSet().members(key)==null?null:redisTemplate.opsForSet().members(key);
	}
	

	/**
	 * hash方式存储
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean hset(String key, String field, Object value) {
		try {
			redisTemplate.opsForHash().put(key, field, value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * hash方式存储
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean hset(String key, String field, Object value,Long expireTime) {
		try {
			redisTemplate.opsForHash().put(key, field, value);
			if(expireTime!=null){
			redisTemplate.expire(key, expireTime, TimeUnit.MILLISECONDS);
			}
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * hash方式获取
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Object hget(String key, String field) {
		return redisTemplate.opsForHash().get(key, field);
	}
	
	/**
	 * hash方式获取
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public Object hget(String key, String field,Long expireTime) {
		if(expireTime!=null){
			redisTemplate.expire(key, expireTime, TimeUnit.MILLISECONDS);
			}
		return redisTemplate.opsForHash().get(key, field);
	}

	/**
	 * 带失效时间的map存储
	 * 
	 * @param key
	 * @param map
	 * @param expireTime
	 * @return
	 */
	public boolean putMap(String key, Map<String, Object> map,
			final long expireTime) {
				try {
			redisTemplate.opsForHash().putAll(key, map);
			redisTemplate.expire(key, expireTime, TimeUnit.DAYS);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	public Object select(String key) {
		try {
			DataType type = redisTemplate.type(key);
			if (DataType.NONE == type) {
				return null;
			} else if (DataType.STRING == type) {
				return redisTemplate.opsForValue().get(key);
			} else if (DataType.LIST == type) {
				return redisTemplate.opsForList().range(key, 0, -1);
			} else if (DataType.HASH == type) {
				return redisTemplate.opsForHash().entries(key);
			}
			if (DataType.SET == type) {
				return redisTemplate.opsForSet().members(key);
			} else
				return null;
		} catch (Exception e) {
			throw new RuntimeException("查询错误:{}", e);
		}
	}

	/**
	 * 精确删除key
	 * 
	 * @param key
	 */
	public void deleteCache(String key) {
		redisTemplate.delete(key);
	}

	/**
	 * 模糊删除key
	 * 
	 * @param pattern
	 */
	public void deleteCacheWithPattern(String pattern) {
		Set<String> keys = redisTemplate.keys(pattern);
		redisTemplate.delete(keys);
	}
	
	/**
	 * 批量删除key
	 * 
	 * @param key
	 */
	public void deleteCacheList(List<String> keys) {
		redisTemplate.delete(keys);
	}

	/**
	 * 清空所有缓存
	 */
	public void clearCache() {
		deleteCacheWithPattern(RedisUtils.CAHCENAME + "|*");

	}

	/**
	 * Object转成JSON数据
	 */
	private String toJson(Object object) {
		if (object instanceof Integer || object instanceof Long
				|| object instanceof Float || object instanceof Double
				|| object instanceof Boolean || object instanceof String) {
			return String.valueOf(object);
		}
		return gson.toJson(object);
	}

	/**
	 * JSON数据，转成Object
	 */
	private <T> T fromJson(String json, Class<T> clazz) {
		return gson.fromJson(json, clazz);
	}

}
